<?php

/**
 * Helper class to trigger template compilation (if needed) just before rendering
 *
 * @author    Mon Zafra <monzee@gmail.com>
 * @package   Yano
 * @copyright (c)2009 Mon Zafra
 * @license   New BSD
 */
class Yano_Template_Compiler extends Zend_Controller_Action_Helper_Abstract
{
    /**
     *
     * @var Yano_Compiler
     */
    protected $_engine;
    protected $_defaultEngineClass = 'Yano_Template_Engine';
    protected $_relativeTemplatePath = '../templates'; // relative to the script path
    protected $_templatePath;
    protected $_templateFile;
    protected $_compiledFile;
    /**
     *
     * @var Zend_Controller_Action_Helper_ViewRenderer
     */
    protected $_viewRenderer;

    /**
     * Constructor
     * @param array$config
     */
    public function __construct($config = array())
    {
    }

    /**
     * Get compiler/instantiate default compiler class
     * @return mixed
     */
    public function getEngine()
    {
        if (empty($this->_engine)) {
            $engine = is_string($this->_engine)
            ? $this->_engine : $this->_defaultEngineClass;
            $this->setEngine(new $engine);
        }
        return $this->_engine;
    }

    /**
     * Set compiler
     * @param mixed $compiler
     * @return Yano_Helper
     */
    public function setEngine($engine)
    {
        $this->_engine = $engine;
        return $this;
    }

    /**
     * Set template path. Will be used only if the absolute path is empty
     * @param string $path Path relative to the view script directory
     * @return Yano_Helper
     */
    public function setRelativeTemplatePath($path)
    {
        $this->_relativeTemplatePath = $path;
        return $this;
    }

    /**
     * Set absolute template path
     * @param string $path
     * @return Yano_Helper
     */
    public function setTemplatePath($path)
    {
        $this->_templatePath = $path;
        return $this;
    }

    /**
     * Should the ViewRenderer render a view script?
     *
     * (lifted from ViewRenderer)
     * @return boolean
     */
    protected function _shouldRender($vr)
    {
        return (!$this->getFrontController()->getParam('noViewRenderer')
            && !$vr->getNeverRender()
            && !$vr->getNoRender()
            && (null !== $vr->_actionController)
            && $this->getRequest()->isDispatched()
            && !$this->getResponse()->isRedirect()
        );
    }

    /**
     * Figure out if template must be compiled
     * @return void
     */
    public function postDispatch()
    {
        $vr = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
        if (!$this->_shouldRender($vr)) {
            return;
        }
        $vr->initView();
        $paths = $vr->view->getScriptPaths();
        $scriptPath = $paths[0];
        $script = $vr->getViewScript();
        $scriptFile = $scriptPath . $script;
        $templatePath = empty($this->_templatePath)
        ? $scriptPath . rtrim($this->_relativeTemplatePath, '\\/')
        : $this->_templatePath;
        $templateFile = $templatePath . DIRECTORY_SEPARATOR . $script;

        // if both script and template don't exist, return and let the error controller handle it
        // if script exists but template doesn't, return and let it render normally
        if (!is_file($templateFile)) {
            return;
        }

        // must compile if the template is readable and the script is unreadable or stale
        if (!is_readable($templateFile)) {
            throw new Yano_Exception('Unable to read template file: ' . $templateFile);
        }
        clearstatcache();
        if (!is_readable($scriptFile) || filemtime($scriptFile) < filemtime($templateFile)) {
            $this->compile($templateFile, $scriptFile);
        }
    }

    /**
     * Compile template file to PHP script
     * @param string $source Filename of template
     * @param string $dest   Filename of script
     */
    public function compile($source, $dest)
    {
        // read template to string
        $template = file_get_contents($source);
        // compile
        $engine = $this->getEngine();
        $compiled = $engine->compile($template);
        // save compiled
        // TODO: check if target directory exists. if not, create it first
        $saved = @file_put_contents($dest, $compiled);
        if (false === $saved) {
            throw new Yano_Exception('Unable to save [' . $compiledFile . ']; Make sure the script directory is writeable');
        }
    }

}
